package proxy;

import java.io.IOException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.MissingResourceException;
import java.util.Set;
import java.util.Timer;
import java.util.Vector;

import message.Response;
import message.response.FileServerInfoResponse;
import message.response.MessageResponse;
import message.response.UserInfoResponse;
import model.FileServerData;
import model.FileServerInfo;
import model.UserInfo;
import model.UserSession;
import util.Config;
import util.IntegrityTool;
import cli.Command;
import cli.Shell;

/**
 * 
 * @author hansjorghofer
 */
public class ProxyCli implements IProxyCli {
	
	private Proxy proxy;
	private Shell shell;
	private Thread shellThread;
	private ProxyManagementComponent proxy_mc;
	private Timer timer;
	private FsListener fileserverListener;
	private ClientDispatcher clientDispatcher;
	
	private IntegrityTool integrityUtil;
	
	public ProxyCli(Proxy proxy, Shell shell) {
		this.proxy = proxy;
		this.shell = shell;
		this.shell.register(this);
		
		timer = new Timer();
		timer.schedule(new FsOnlineReviser(this.proxy), 0, this.proxy.getCheckPeriod());
		
		fileserverListener = new FsListener(this.proxy);
		clientDispatcher = new ClientDispatcher(this.proxy);
		shellThread = new Thread(this.shell, "ShellThread");
		proxy_mc = new ProxyManagementComponent(this.proxy, new Config("mc"));
		proxy.setManagementComponent(proxy_mc);
				
		try {
			integrityUtil = new IntegrityTool(new Config("proxy"));
		} catch (IOException e) {
			e.printStackTrace();
		}
		
		fileserverListener.start();
		clientDispatcher.start();
		shellThread.start();
		proxy_mc.start();
	}
	
	@Command
	public Response workers() throws IOException {
		return new MessageResponse(this.clientDispatcher.workerList());
	}
	
	@Command
	public Response threads() {
		String response = "Running Threads:";
		Set<Thread> threads = Thread.getAllStackTraces().keySet();
		for(Thread t : threads) {
			response +=  "\n" + t.getName() + " Status: " + (t.isAlive() ? "alive" : "dead");
		}
		return new MessageResponse(response);
	}
	
	@Override
	@Command
	public Response fileservers() throws IOException {
		
		Vector<FileServerInfo> fsInfos = new Vector<FileServerInfo>();
		
		for(FileServerData fs : proxy.getFileServers())
			fsInfos.add(fs.getFileServerInfo());
		
		return new FileServerInfoResponse(fsInfos);
	}

	@Override
	@Command
	public Response users() throws IOException {
		
		Vector<UserInfo> userInfos = new Vector<UserInfo>();
		
		for(UserSession session : proxy.getUserSessions())
			userInfos.add(session.getUserInfo());
		
		return new UserInfoResponse(userInfos);
	}

	@Override
	@Command
	public MessageResponse exit() throws IOException {
		
		fileserverListener.shutdown();
		clientDispatcher.shutdown();
		proxy_mc.shutdown();
		timer.cancel();
		shellThread.interrupt();
		shell.close();
		System.in.close();
		
		return new MessageResponse("Shutting down...");
	}
	
	@Command
	public MessageResponse hmac(String message) throws IOException, InvalidKeyException, NoSuchAlgorithmException {
		
		String hmac = integrityUtil.createMac(message);
		
		return new MessageResponse(hmac);
	}
	
	@Command
	public MessageResponse verify(String message, String hmac) throws InvalidKeyException, NoSuchAlgorithmException {
		
		boolean same = integrityUtil.verifyMac(message, hmac);
		
		if(same)
			return new MessageResponse("Message OK");
		else
			return new MessageResponse("Message Corrupted");
	}
		
	public static void main(String[] args) {
		try {
			Config config = new Config("proxy");
			Proxy proxy = new Proxy(config);
			Shell shell = new Shell("proxy", System.out, System.in);
			
			new ProxyCli(proxy, shell);
			
		} catch (MissingResourceException e) {
			System.out.println("Proxy could not start: Missing resources.");
		}
	}

	@Override
	public Proxy getProxy() {
		return this.proxy;
	}
	

}
